package org.hawk.gwt.ppc;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.hawk.gwt.ppc.utils.AntPattern;
import org.hawk.gwt.ppc.utils.JavaPattern;
import org.hawk.gwt.ppc.utils.Pattern;

/**
 * Represents configuration parser from {@link System#out} command line.
 * 
 * @author alex.bereznevatiy@gmail.com
 */
class CmdArgs implements Configuration {
	private static final String JAVA_PATTERN = "java";
	private static final String ANT_PATTERN = "ant";
	private static final String ARG_PREFIX = "-P";
	private static final String GWT_ARG_PREFIX = "-";
	private static final Set<String> GWT_EMPTY_ARGS;
	
	static{
		Set<String> rez = new HashSet<String>();
		rez.add("-ea");
		rez.add("-XdisableClassMetadata");
		rez.add("-XdisableCastChecking");
		rez.add("-validateOnly");
		rez.add("-draftCompile");
		rez.add("-compileReport");
		GWT_EMPTY_ARGS = Collections.unmodifiableSet(rez);
	}
	
	private String[] compilerArguments;
	private Map<String,Object> params;

	CmdArgs(String[] args) {
		compilerArguments = extractCompilerArguments(args);
		params = parseArguments(args);
	}

	private static String[] extractCompilerArguments(String[] args) {
		List<String> result = new LinkedList<String>();
		for(String arg : args){
			if(!arg.startsWith(ARG_PREFIX)){
				result.add(arg);
			}
		}
		return result.toArray(new String[result.size()]);
	}
	
	private static Map<String,Object> parseArguments(String[] args){
		Map<String,Object> result = new HashMap<String, Object>();
		for(int i=0;i<args.length;i++){
			if(args[i]==null){
				throw new IllegalArgumentException("NULL");
			}
			else if(args[i].startsWith(ARG_PREFIX)){
				parseArgument(args[i],result);
			}
			else {
				String name = args[i];
				Object value = null;
				if(GWT_EMPTY_ARGS.contains(name)){
					value = Boolean.TRUE;
				}
				else {
					i++;
					value = i<args.length?args[i]:"";
				}
				
				result.put(name, value);
			}
		}
		return Collections.unmodifiableMap(result);
	}
	
	private static void parseArgument(String arg,Map<String,Object> result) {
		arg = arg.substring(ARG_PREFIX.length());
		if(arg.indexOf('=')<0){
			result.put(arg, Boolean.TRUE);
			return;
		}
		String[] tmp = arg.split("=",2);
		try{
			result.put(tmp[0],Integer.valueOf(tmp[1]));
		}
		catch(NumberFormatException ex){
			result.put(tmp[0],tmp[1]);
		}
	}

	/**
	 * @return arguments to GWT compiler. All processor-specific
	 * arguments will be omitted.
	 */
	public String[] getCompillerArguments() {
		return compilerArguments;
	}
	/**
	 * @return list of GWT modules to process.
	 */
	public String[] getModules(){
		List<String> modules = new LinkedList<String>();
		for(int i=0;i<compilerArguments.length;i++){
			if(!compilerArguments[i].startsWith(GWT_ARG_PREFIX)){
				modules.add(compilerArguments[i]);
			}
			else if(!GWT_EMPTY_ARGS.contains(compilerArguments[i])){
				i++;
			}
		}
		return modules.toArray(
			new String[modules.size()]);
	}

	public String get(String name) {
		Object rez = get(name,null);
		return rez==null?null:rez.toString();
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.Configuration#get(java.lang.String, java.lang.Object)
	 * TODO: refactor me
	 */
	@SuppressWarnings("unchecked")
	public <T> T get(String name, T defaultValue) {
		Object result = params.get(name);
		if(result!=null&&(defaultValue instanceof String)){
			return (T) result.toString();
		}
		if(result!=null&&(defaultValue instanceof Boolean)){
			return (T)Boolean.valueOf(result.toString());
		}
		try {
			if(result==null){
				return defaultValue;
			}
			return (T)result;
		}
		catch(ClassCastException ex){}
		try{
			return (T) result.toString();
		}
		catch(ClassCastException ex){}
		return defaultValue;
	}

	public Pattern getNewPattern(String p) {
		try {
			return getPatternClass().getConstructor(String.class).newInstance(p);
		}
		catch(Exception ex){
			Log.warn("Unable to create pattern because of exception: "+ex.toString());
		}
		return new AntPattern(p);
	}

	@SuppressWarnings("unchecked")
	private Class<? extends Pattern> getPatternClass() throws ClassNotFoundException {
		String pattern = get(PATTERN_PARAMETER,ANT_PATTERN);
		if(pattern.equals(ANT_PATTERN)){
			return AntPattern.class;
		}
		else if(pattern.equals(JAVA_PATTERN)){
			return JavaPattern.class;
		}
		return (Class<? extends Pattern>)Class.forName(pattern);
	}
}
